package com.neusoft.databus.policy.expression.impl;

import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.jexl3.JexlContext;
import org.apache.commons.jexl3.MapContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.google.common.collect.Lists;
import com.neusoft.bizcore.common.cache.CacheService;
import com.neusoft.bizcore.web.exception.CustomRuntimeException;
import com.neusoft.databus.common.bean.MetricBean;
import com.neusoft.databus.common.bean.ResourceBean;
import com.neusoft.databus.common.constant.DatabusErrorCode;
import com.neusoft.databus.policy.constants.DatabusPolicyConstant;
import com.neusoft.databus.policy.enums.VariableType;
import com.neusoft.databus.policy.expression.PolicyExpressionParser;
import com.neusoft.databus.policy.utils.ExpressionCalculator;
import com.neusoft.databus.policy.utils.PolicyUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * 表达式解析类
 *
 * @author naxiang
 */
@Component
@Slf4j
public class PolicyExpressionParserImpl implements PolicyExpressionParser {

    @Autowired
    private CacheService cacheService;

    @Override
    public boolean valueCompare(final Map<String, Object> expression, final ResourceBean resourceBean)
            throws Exception {
        boolean result = false;
        final Object metricValue = this.getMetricValue(resourceBean,
                String.valueOf(expression.get(DatabusPolicyConstant.METRICCODE)));
        if (metricValue == null) {
            PolicyExpressionParserImpl.log.error("资源{}不存在NEW指标{}的值", resourceBean.getCode(),
                    expression.get(DatabusPolicyConstant.METRICCODE).toString());
            throw new CustomRuntimeException(DatabusErrorCode.ERR_CODE_406221);
        } else {
            final StringBuffer expressionStr = new StringBuffer();
            expressionStr.append(expression.get(DatabusPolicyConstant.METRICCODE))
                    .append(expression.get(DatabusPolicyConstant.SIGN))
                    .append(DatabusPolicyConstant.THRESHOLD);
            final JexlContext jexlContext = new MapContext();
            jexlContext.set((String) expression.get(DatabusPolicyConstant.METRICCODE), metricValue);
            jexlContext.set(DatabusPolicyConstant.THRESHOLD,
                    expression.get(DatabusPolicyConstant.THRESHOLD));
            result = ExpressionCalculator.getJexlCalculatorResult(expressionStr.toString(), jexlContext);
        }
        return result;
    }

    @Override
    public boolean valueChange(final Map<String, Object> expression, final ResourceBean resourceBean,
            final Long policyId)
            throws Exception {
        boolean result = false;
        final Object metricValue = this.getMetricValue(resourceBean,
                expression.get(DatabusPolicyConstant.METRICCODE).toString());
        final Object oldMetricValue = this.getOldMetricValue(resourceBean,
                expression.get(DatabusPolicyConstant.METRICCODE).toString(), policyId);
        if ((metricValue == null) || (oldMetricValue == null)) {
            PolicyExpressionParserImpl.log.error("资源{}不存在NEW或者OLD指标{}的值", resourceBean.getCode(),
                    expression.get(DatabusPolicyConstant.METRICCODE).toString());
            throw new CustomRuntimeException(DatabusErrorCode.ERR_CODE_406221);
        } else {
            if (Double.parseDouble(metricValue.toString()) != Double.parseDouble(oldMetricValue.toString())) {
                result = true;
            }
        }
        return result;
    }

    @Override
    public boolean valuePercentageChange(final Map<String, Object> expression, final ResourceBean resourceBean,
            final Long policyId)
            throws Exception {
        boolean result = false;
        final Object metricValue = this.getMetricValue(resourceBean,
                expression.get(DatabusPolicyConstant.METRICCODE).toString());
        final Object oldMetricValue = this.getOldMetricValue(resourceBean,
                expression.get(DatabusPolicyConstant.METRICCODE).toString(), policyId);
        if ((metricValue == null) || (oldMetricValue == null)) {
            PolicyExpressionParserImpl.log.error("资源{}不存在NEW或者OLD指标{}的值", resourceBean.getCode(),
                    expression.get(DatabusPolicyConstant.METRICCODE).toString());
            throw new CustomRuntimeException(DatabusErrorCode.ERR_CODE_406221);
        } else {
            final StringBuffer expressionStr = new StringBuffer();
            expressionStr.append("(").append(metricValue).append("-").append(oldMetricValue).append(")/")
                    .append(metricValue).append(expression.get(DatabusPolicyConstant.SIGN))
                    .append(expression.get(DatabusPolicyConstant.THRESHOLD));
            result = ExpressionCalculator.getJexlCalculatorResult(expressionStr.toString(), null);
        }
        return result;
    }

    @Override
    public boolean valueScopeChange(final Map<String, Object> expression, final ResourceBean resourceBean,
            final Long policyId)
            throws Exception {
        boolean result = false;
        final Object metricValue = this.getMetricValue(resourceBean,
                expression.get(DatabusPolicyConstant.METRICCODE).toString());
        final Object oldMetricValue = this.getOldMetricValue(resourceBean,
                expression.get(DatabusPolicyConstant.METRICCODE).toString(), policyId);
        if ((metricValue == null) || (oldMetricValue == null)) {
            PolicyExpressionParserImpl.log.error("资源{}不存在NEW或者OLD指标{}的值", resourceBean.getCode(),
                    expression.get(DatabusPolicyConstant.METRICCODE).toString());
            throw new CustomRuntimeException(DatabusErrorCode.ERR_CODE_406221);
        } else {
            final StringBuffer expressionStr = new StringBuffer();
            expressionStr.append(metricValue).append("-").append(oldMetricValue)
                    .append(expression.get(DatabusPolicyConstant.SIGN))
                    .append(expression.get(DatabusPolicyConstant.THRESHOLD));
            result = ExpressionCalculator.getJexlCalculatorResult(expressionStr.toString(), null);
        }
        return result;
    }

    @Override
    public boolean custom(final Map<String, Object> expression, final ResourceBean resourceBean, final Long policyId)
            throws Exception {
        final List<String> variableList = this.getVariableList(expression);
        final JexlContext jexlContext = this.getJexlContext(policyId, variableList, resourceBean);
        final boolean result = ExpressionCalculator.getJexlCalculatorResult(
                expression.get(DatabusPolicyConstant.EXPRESSION).toString(), jexlContext);
        return result;
    }

    /**
     * 获取当前指标值
     *
     * @param resourceBean
     * @param metricCode
     * @return
     */
    private Object getMetricValue(final ResourceBean resourceBean, final String metricCode) {
        final List<MetricBean> metrics = resourceBean.getMetrics();
        final Object value =
                metrics.stream().filter(it -> metricCode.equalsIgnoreCase(it.getCode())).findFirst().get().getValue();

        if (value == null) {
            PolicyExpressionParserImpl.log.error("资源{}的指标{}值不存在", resourceBean.getCode(), metricCode);
        }
        return value;
    }

    /**
     * 获取表达式变量列表
     *
     * @param expression
     * @return
     * @author hanbq
     * @date 2015-10-10 下午5:32:06
     */
    private List<String> getVariableList(final Map<String, Object> expression) {
        final List<String> variableList = Lists.newArrayList();
        // 查找规则公式中#号以内的字符
        final Pattern p = Pattern.compile("#.*?#");
        final Matcher m = p.matcher(expression.get(DatabusPolicyConstant.EXPRESSION).toString());
        while (m.find()) {
            final String param = m.group().replaceAll("#", "");// 去掉#
            variableList.add(param);
        }
        return variableList;
    }

    /**
     * 获取上一次指标值
     *
     * @param resourceBean
     * @param metricCode
     * @param policyId
     * @return
     */
    private Object getOldMetricValue(final ResourceBean resourceBean, final String metricCode, final Long policyId) {
        final String resourceCode = resourceBean.getCode();
        final List<MetricBean> metrics = resourceBean.getMetrics();
        MetricBean metricData = null;
        for (final MetricBean metricBean : metrics) {
            if (metricCode.equalsIgnoreCase(metricBean.getCode())) {
                metricData = metricBean;
                break;
            }
        }
        if (metricData == null) {
            PolicyExpressionParserImpl.log.error("资源{}的指标{}的值不存在", resourceCode, metricCode);
            return null;
        }
        final String key = PolicyUtil.getOldMetricValueKey(resourceBean.getCode(), policyId, metricCode);
        final String cacheName = DatabusPolicyConstant.POLICY_EXPRESSION_CACHE_NAME;
        if (!this.cacheService.isInCache(cacheName, key)) {
            this.cacheService.put(cacheName, key, metricData);
            PolicyExpressionParserImpl.log.error("资源{}上一次采集的指标{}的值不存在", resourceCode, metricCode);
            return null;
        }
        final MetricBean oldMetricData = (MetricBean) this.cacheService.get(cacheName, key);
        this.cacheService.put(cacheName, key, metricData);
        final Object value = oldMetricData.getValue();
        return value;
    }

    /**
     * 获取表达式变量与值映射（变量拼装“#”）
     *
     * @param policyId
     * @param variableList
     * @param resourceBean
     * @return
     */
    private JexlContext getJexlContext(final Long policyId, final List<String> variableList,
            final ResourceBean resourceBean) {

        final JexlContext jexlContext = new MapContext();
        out: for (final String variable : variableList) {
            final String vType = variable.substring(0, 3);
            final VariableType vTypeE = VariableType.valueOf(vType);
            if (vTypeE == null) {
                PolicyExpressionParserImpl.log.error("不存在该数据[{}]解析方法", vType);
                continue out;
            }
            final String metricName = variable.substring(3, variable.length());
            in: switch (vTypeE) {
            case VMC:
                jexlContext.set(variable, this.getMetricValue(resourceBean, metricName));
                break in;
            case VMO:
                jexlContext.set(variable, this.getOldMetricValue(resourceBean, metricName, policyId));
                break in;
            }
        }
        return jexlContext;
    }

}
